package chenjiuxu.seniorui.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;

/**
 * Created by 15705 on 2018/1/5.
 * 绘制音频波形图
 */
public class AudioWaveView extends View {
    private final Paint paint;
    private final Path path;
    private ArrayList<Short> audioData;//音频数据集合
    private int height = -1;
    private int width = -1;
    private boolean drawWave = false;
    private float move = 0;//实时滑动距离
    private int SCROLL = 0;//滑动距离
    private float dotCount;//最大显示采样点
    private float t;//每一个采样点时间
    private float time;//时间轴


    private int colorLine = 0xFF5ABFC1;//中线颜色
    private int colorChoose = 0x88888888;//未选择部分颜色
    private int colorText = Color.RED;//未选择部分颜色
    private float zoom = 2f;//缩放比例 每一个采样点距离


    public AudioWaveView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(1);
        path = new Path();
        audioData = new ArrayList<>();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (height == -1) {
            height = getHeight();
            width = getWidth();
            dotCount = getDotCount();
        }
        //绘制中线
        paint.setColor(colorLine);
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawLine(0, height / 2, width, height / 2, paint);
        //绘制音波
        drawWave(canvas);
        //绘制阴影部分
        paint.setColor(colorChoose);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawRect(0, 0, width / 2, height, paint);
        //绘制时间文字
        drawText(canvas);
    }

    /**
     * 绘制时间
     */
    private void drawText(Canvas canvas) {
        if (drawWave) {
            String ts = time + "S";
            paint.setTextSize(30);
            paint.setColor(colorText);
            float s = width / 2 - paint.measureText(ts) / 2;
            canvas.drawText(ts, s, 40, paint);
        }
    }

    /**
     * 根据滑动时间
     */
    private void getCurrentTime() {
        float index = getIndex();
        time = ((int) (index * t * 10)) / 10f;
    }

    /**
     * 绘制波形
     */
    private void drawWave(Canvas canvas) {
        if (drawWave) {
            path.reset();
            path.moveTo(0, height / 2);
            int index = (int)getIndex();
            float size = dotCount + index;
            if (size > audioData.size()) size = audioData.size();
            for (int i = index; i < size; i++)
                path.lineTo((i - index) * zoom, height / 2 + audioData.get(i));
            canvas.drawPath(path, paint);
        }
    }


    /**
     * 根据移动距离index
     */
    private float getIndex() {
        float i =  (move * -1 / zoom);
        if (i < 0) i = 0;
        return i;
    }


    /**
     * 计数view宽度内能显示多少采样点
     */
    private float getDotCount() {
        return width / zoom;
    }

    float d;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!drawWave) return true;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                d = event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                move = SCROLL + (event.getX() - d);
                if (move > width / 2) move = width / 2;
                getCurrentTime();
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                SCROLL = (int) move;
                getCurrentTime();
                invalidate();
                break;
        }
        return true;
    }

    public void startGetWaveData(String pata) {

        DecodeAudio decodeAudio = new DecodeAudio();
        decodeAudio.startGetData(pata);
    }

    /**
     * 用于处理音频解码成PCM格式
     */
    public class DecodeAudio {
        private final boolean isBigEnd;
        private short keep = 0;
        private final float scale;//比例
        private int sampleRate;//采样率
        private long durationUs;//时长
        private float newSampleRate;//新采样率
        private String cacheFile;//缓存路径
        private File file;

        public DecodeAudio() {
            isBigEnd = !isBigEnd();
            //   scale = width / 65536f;
            scale = 525 / 65536f;
        }

        public void startGetData(String path) {
            cacheFile = getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES).getPath();
            file = new File(cacheFile, new File(path).getName() + "AUDIO_DATA");

            if (file.exists()) {//判断缓存文件是否存在
                getAudioFormat(path);
                getCacheData();
            } else {//解码数据
                initAudio(path);
            }
        }

        //获取缓存数据
        private void getCacheData() {
            new Thread() {
                @Override
                public void run() {
                    try (FileInputStream inputStream = new FileInputStream(file)) {
                        byte[] buf = new byte[2];
                        while ((inputStream.read(buf)) != -1) {
                            short s = byteToShort(buf);
                            if (tag && audioData.size() > dotCount * 3) {
                                tag = false;
                                drawWave = true;
                                for (int i = 0; i < dotCount / 2; i++) audioData.add(0, (short) 0);
                                postInvalidate();
                            }
                            audioData.add(s);
                        }
                        drawWave = true;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }

        public short byteToShort(byte[] b) {
            short s;
            short s0 = (short) (b[0] & 0xff);// 最低位
            short s1 = (short) (b[1] & 0xff);
            s1 <<= 8;
            s = (short) (s0 | s1);
            return s;
        }

        private void getAudioFormat(String path) {
            MediaExtractor extractor = new MediaExtractor();//文件提取器
            try {
                extractor.setDataSource(path);//设置文件路径
            } catch (IOException e) {
                extractor.release();
                extractor = null;
            }
            if (extractor == null) return;
            MediaFormat audioFormat = null;//音频格式
            int track = 0;//音轨下标
            int trackCount = extractor.getTrackCount();//获取文件多媒体轨道
            for (int i = 0; i < trackCount; i++) {//遍历轨道找到音频轨道
                MediaFormat f = extractor.getTrackFormat(i);
                String m = f.getString(MediaFormat.KEY_MIME);
                if (m.startsWith("audio/")) {
                    audioFormat = f;//音轨格式
                    track = i;//音轨
                    break;
                }
            }
            extractor.selectTrack(track);//选择读取音频轨道
            sampleRate = audioFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
            newSampleRate = sampleRate / 100f;
            AudioWaveView.this.t = 1f / sampleRate * newSampleRate;
            durationUs = audioFormat.getLong(MediaFormat.KEY_DURATION);
        }

        //初始解码工作
        private void initAudio(String path) {
            MediaExtractor extractor = new MediaExtractor();//文件提取器
            try {
                extractor.setDataSource(path);//设置文件路径
            } catch (IOException e) {
                extractor.release();
                extractor = null;
            }
            if (extractor == null) return;
            MediaFormat audioFormat = null;//音频格式
            String mime = null;
            int track = 0;//音轨下标
            int trackCount = extractor.getTrackCount();//获取文件多媒体轨道
            for (int i = 0; i < trackCount; i++) {//遍历轨道找到音频轨道
                MediaFormat f = extractor.getTrackFormat(i);
                String m = f.getString(MediaFormat.KEY_MIME);
                if (m.startsWith("audio/")) {
                    audioFormat = f;//音轨格式
                    track = i;//音轨
                    mime = m;
                    break;
                }
            }
            extractor.selectTrack(track);//选择读取音频轨道
            sampleRate = audioFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
            newSampleRate = sampleRate / 100f;
            AudioWaveView.this.t = 1f / sampleRate * newSampleRate;
            durationUs = audioFormat.getLong(MediaFormat.KEY_DURATION);
            if (audioFormat == null) return;
            //初始解码器
            MediaCodec mediaCodec = null;
            try {
                mediaCodec = MediaCodec.createDecoderByType(mime);
            } catch (IOException e) {
                mediaCodec.stop();
                mediaCodec.release();
                mediaCodec = null;
            }
            mediaCodec.configure(audioFormat, null, null, 0);//配置解码器

            final MediaCodec finalMediaCodec = mediaCodec;
            final MediaExtractor finalExtractor = extractor;
            new Thread() {
                @Override
                public void run() {
                    decode(finalMediaCodec, finalExtractor);
                    cache();
                }
            }.start();


        }

        //缓存
        private void cache() {
            FileOutputStream outputStream = null;
            try {
                boolean isNew = file.createNewFile();
                if (!isNew) throw new RuntimeException("无法创建缓存文件");

                outputStream = new FileOutputStream(file);
                byte[] bytes = new byte[2];
                for (int i = 0; i < audioData.size(); i++) {
                    Short s = audioData.get(i);
                    if (s != null) {
                        outputStream.write(shortToByte(s, bytes));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                file.delete();
            } finally {
                try {
                    if (outputStream != null) outputStream.close();
                } catch (Exception e) {
                    file.delete();
                    e.printStackTrace();
                }
            }
        }

        public byte[] shortToByte(short number, byte[] b) {
            int temp = number;
            for (int i = 0; i < b.length; i++) {
                b[i] = new Integer(temp & 0xff).byteValue();// 将最低位保存在最低位
                temp = temp >> 8; // 向右移8位
            }
            return b;
        }


        boolean tag = true;

        //开始解码
        private void decode(MediaCodec mediaCodec, MediaExtractor extractor) {
            mediaCodec.start();//开始解码
            MediaCodec.BufferInfo decode_info = new MediaCodec.BufferInfo();//用于描述解码得到的byte[]数据的相关信息
            extractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);//从头读取文件
            while (true) {//解码
                int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);//获取
                if (inputBufferIndex < 0) break;
                ByteBuffer inbuffer = mediaCodec.getInputBuffer(inputBufferIndex);
                inbuffer.clear();
                int sampleSize = extractor.readSampleData(inbuffer, 0);//读取数据放入解码器
                if (sampleSize < 0) break;//sampleSize读取完成
                mediaCodec.queueInputBuffer(inputBufferIndex, 0, sampleSize, 0, 0);//通知解码器数据放入
                extractor.advance();//读取下一步
                int outputBufferIndex = mediaCodec.dequeueOutputBuffer(decode_info, 1000);//获取解码数据
                while (outputBufferIndex >= 0) {
                    ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex);//获取解析数据
                    if (decode_info.size != 0) {
                        outputBuffer.position(decode_info.offset);
                        outputBuffer.limit(decode_info.offset + decode_info.size);
                        byte[] data = new byte[decode_info.size];
                        outputBuffer.get(data);//读取buffer数据
                        if (tag && audioData.size() > dotCount * 3) {
                            tag = false;
                            drawWave = true;
                            for (int i = 0; i < dotCount / 2; i++) audioData.add(0, (short) 0);
                            postInvalidate();
                        }
                        //处理MPC数据
                        if (isBigEnd) {
                            byteArray2ShortArrayLittle(data, data.length / 2);
                        } else {
                            byteArray2ShortArrayBig(data, data.length / 2);
                        }

                    }
                    mediaCodec.releaseOutputBuffer(outputBufferIndex, false);//此操作一定要做，不然MediaCodec用完所有的Buffer后 将不能向外输出数据
                    outputBufferIndex = mediaCodec.dequeueOutputBuffer(decode_info, 1000);
                }
            }
            extractor.release();
            mediaCodec.stop();
            mediaCodec.release();
        }

        private boolean isBigEnd() {
            short i = 0x1;
            boolean bRet = ((i >> 8) == 0x1);
            return bRet;
        }

        //每一个采样点是 2byte 合成为一个
        private void byteArray2ShortArrayBig(byte[] data, int items) {
            short[] retVal = new short[items];
            for (int i = 0; i < retVal.length; i++) {
                retVal[i] = (short) ((data[i * 2 + 1] & 0xff) | (data[i * 2] & 0xff) << 8);
                record(retVal[i]);
            }
        }

        //每一个采样点是 2byte 合成为一个
        private void byteArray2ShortArrayLittle(byte[] data, int items) {
            short[] retVal = new short[items];
            for (int i = 0; i < retVal.length; i++) {
                retVal[i] = (short) ((data[i * 2] & 0xff) | (data[i * 2 + 1] & 0xff) << 8);
                record(retVal[i]);
            }
        }

        //原音频采样率降低100
        private void record(short data) {
            if (keep > newSampleRate) keep = 0;
            if (keep == 0) {
                audioData.add((short) (data * scale * -1));
            }
            keep++;
        }
    }


}
